<?php

/*
 * 实现个简单的select对像
 * 目前只实现于core_db的select
 */
class dbeav_select {

    const DISTINCT = 'distinct';

    const COLUMNS = 'columns';

    const FROM = 'from';

    const WHERE = 'where';

    const GROUP = 'group';

    const HAVING = 'having';

    const ORDER = 'order';

    const LIMIT_COUNT = 'limit_count';

    const LIMIT_OFFSET = 'limit_offset';

    const SQL_WILDCARD = '*';

    const SQL_SELECT = 'SELECT';

    const SQL_FROM = 'FROM';

    const SQL_WHERE = 'WHERE';

    const SQL_DISTINCT = 'DISTINCT';

    const SQL_GROUP_BY = 'GROUP BY';

    const SQL_ORDER_BY = 'ORDER BY';

    const SQL_HAVING = 'HAVING';

    const SQL_AND = 'AND';

    const SQL_AS = 'AS';

    const SQL_OR = 'OR';

    const SQL_ON = 'ON';

    const SQL_ASC = 'ASC';

    const SQL_DESC = 'DESC';

    protected static $_init_parts = array(
        self::DISTINCT => false,
        self::COLUMNS => array(),
        self::FROM => null,
        self::WHERE => array(),
        self::GROUP => array(),
        self::HAVING => array(),
        self::ORDER => array(),
        self::LIMIT_COUNT => null,
        self::LIMIT_OFFSET => null
    );

    private $_parts = array();

    public function set_model(&$model) {
        $this->_model = $model;
        return $this;
    }
 // End Function
    public function get_model() {
        return $this->_model;
    }
 // End Function
    public function set_adapter($adapter) {
        $this->_adapter = $adapter;
        return $this;
    }
 // End Function
    public function get_parts() {
        return $this->_parts;
    }
 // End Function
    public function reset($part = null) {
        if ($part == null) {
            $this->_parts = self::$_init_parts;
        } else if (array_key_exists($part, self::$_init_parts)) {
            $this->_parts[$part] = self::$_init_parts[$part];
        }
        return $this;
    }
 // End Function
    public function distinct($flag = true) {
        $this->_parts[self::DISTINCT] = (bool) $flag;
        return $this;
    }
 // End Function
    public function from($db, $cols = self::SQL_WILDCARD) {
        $this->_parts[self::FROM] = $db;
        $this->columns($cols);
        return $this;
    }
 // End Function
    public function columns($cols = self::SQL_WILDCARD) {
        $this->reset('columns');
        if (! is_array($cols))
            $cols = array(
                $cols
            );
        foreach ($cols as $alias => $col) {
            $this->_parts[self::COLUMNS][] = array(
                $col,
                is_string($alias) ? $alias : null
            );
        }
        return $this;
    }
 // End Function
    public function where($str, $val = '') {
        $this->_parts[self::WHERE][] = $this->_where($str, $val, true);
        return $this;
    }
 // End Function
    public function or_where($str, $val = '') {
        $this->_parts[self::WHERE][] = $this->_where($str, $val, false);
        return $this;
    }
 // End Function
    private function _where($str, $val, $bool) {
        $str = (isset($val)) ? $this->_adapter->quote_into($str, $val) : $str;
        return ($bool) ? self::SQL_AND . ' ' . $str : self::SQL_OR . ' ' . $str;
    }
 // End Function
    public function group($groups) {
        if (! is_array)
            $groups = array(
                $groups
            );
        foreach ($groups as $group) {
            $this->_parts[self::GROUP][] = $group;
        }
        return $this;
    }
 // End Function
    public function having($str) {
        if (func_num_args() > 1) {
            $str = $this->_adapter->quote_into($str, func_get_arg(1));
        }
        if ($this->_parts[self::HAVING]) {
            $this->_parts[self::HAVING][] = self::SQL_AND . " " . $str;
        } else {
            $this->_parts[self::HAVING][] = $str;
        }
        return $this;
    }
 // End Function
    public function or_having($str) {
        if (func_num_args() > 1) {
            $str = $this->_adapter->quote_into($str, func_get_arg(1));
        }
        if ($this->_parts[self::HAVING]) {
            $this->_parts[self::HAVING][] = self::SQL_OR . " " . $str;
        } else {
            $this->_parts[self::HAVING][] = $str;
        }
        return $this;
    }
 // End Function
    public function order($orders) {
        if (! is_array($orders))
            $orders = array(
                $orders
            );
        
        foreach ($orders as $order) {
            $direction = self::SQL_ASC;
            if (preg_match('/(.*\W)(' . self::SQL_ASC . '|' . self::SQL_DESC . ')\b/si', $order, $matches)) {
                $val = trim($matches[1]);
                $direction = strtoupper($matches[2]);
            }
            $this->_parts[self::ORDER][] = array(
                $val,
                $direction
            );
        }
        return $this;
    }
 // End Function
    public function limit($offset = null, $count = null) {
        $this->_parts[self::LIMIT_COUNT] = (int) $count;
        $this->_parts[self::LIMIT_OFFSET] = (int) $offset;
        return $this;
    }
 // End Function
    public function page($page = null, $size = null) {
        $page = ($page > 0) ? $page : 1;
        $size = ($size > 0) ? $size : 1;
        $this->_parts[self::LIMIT_COUNT] = (int) $size;
        $this->_parts[self::LIMIT_OFFSET] = (int) $size * ($page - 1);
        return $this;
    }
 // End Function
    public function instance() {
        $this->_adapter->set_obj($this);
        return $this->_adapter;
    }
 // End Function
    public function assemble() {
        $sql = self::SQL_SELECT;
        foreach (array_keys(self::$_init_parts) as $part) {
            $part = strtolower($part);
            if ($part == 'limit_count')
                continue; // todo：减少判断methon_exists提高性能
            $method = 'render_' . $part;
            // if (method_exists($this, $method)) {
            $sql = $this->$method($sql);
            // }
        }
        return $sql;
    }
 // End Function
    protected function render_distinct($sql) {
        if ($this->_parts[self::DISTINCT]) {
            $sql .= ' ' . self::SQL_DISTINCT;
        }
        return $sql;
    }
 // End Function
    protected function render_columns($sql) {
        if (! count($this->_parts[self::COLUMNS])) {
            return null;
        }
        $columns = array();
        foreach ($this->_parts[self::COLUMNS] as $columnEntry) {
            list ($column, $alias) = $columnEntry;
            $columns[] = $this->_adapter->quote_column_as($column, $alias);
        }
        return $sql .= ' ' . implode(', ', $columns);
    }
 // End Function
    public function get_columns() {
        $columns = array();
        if (count($this->_parts[self::COLUMNS])) {
            foreach ($this->_parts[self::COLUMNS] as $columnEntry) {
                list ($column, $alias) = $columnEntry;
                $columns[] = $this->_adapter->quote_column_as($column, $alias);
            }
        }
        return $columns;
    }
 // End Function
    protected function render_from($sql) {
        if (! empty($this->_parts[self::FROM])) {
            $sql .= ' ' . self::SQL_FROM . ' ' . $this->_adapter->quote_identifier($this->_parts[self::FROM]);
        }
        return $sql;
    }
 // End Function
    protected function render_where($sql) {
        if ($this->_parts[self::FROM] && $this->_parts[self::WHERE]) {
            $sql .= ' ' . self::SQL_WHERE . ' 1=1 ' . implode(' ', $this->_parts[self::WHERE]);
        }
        return $sql;
    }
 // End Function
    protected function render_group($sql) {
        if ($this->_parts[self::FROM] && $this->_parts[self::GROUP]) {
            $group = array();
            foreach ($this->_parts[self::GROUP] as $term) {
                $group[] = $this->_adapter->quote_identifier($term);
            }
            $sql .= ' ' . self::SQL_GROUP_BY . ' ' . implode(",\n\t", $group);
        }
        return $sql;
    }
 // End Function
    protected function render_having($sql) {
        if ($this->_parts[self::FROM] && $this->_parts[self::HAVING]) {
            $sql .= ' ' . self::SQL_HAVING . ' ' . implode(' ', $this->_parts[self::HAVING]);
        }
        return $sql;
    }
 // End Function
    protected function render_order($sql) {
        if ($this->_parts[self::ORDER]) {
            $order = array();
            foreach ($this->_parts[self::ORDER] as $term) {
                if (is_array($term)) {
                    $order[] = $this->_adapter->quote_identifier($term[0]) . ' ' . $term[1];
                } else {
                    $order[] = $this->_adapter->quote_identifier($term);
                }
            }
            $sql .= ' ' . self::SQL_ORDER_BY . ' ' . implode(', ', $order);
        }
        return $sql;
    }
 // End Function
    protected function render_limit_offset($sql) {
        $count = 0;
        $offset = 0;
        if (! empty($this->_parts[self::LIMIT_OFFSET])) {
            $offset = (int) $this->_parts[self::LIMIT_OFFSET];
            $count = intval(9223372036854775807); // php最大值
        }
        if (! empty($this->_parts[self::LIMIT_COUNT])) {
            $count = (int) $this->_parts[self::LIMIT_COUNT];
        }
        if ($count > 0) {
            $sql = trim($this->_adapter->limit($sql, $count, $offset));
        }
        return $sql;
    }
 // End Function
    public function to_sql() {
        return $this->assemble();
    } // End Function
}//End Class
